/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  spi_test.c
 *    Description:  This file used to test LCD 设计一个自定义速率和数据的spi回环测试测试程序.
 *                 
 *        Version:  1.0.0(2023年07月03日)
 *         Author:  Ling Yun <lingyun@email.com>
 *      ChangeLog:  1, Release initial version on "2023年07月03日 19时42分04秒"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

typedef struct spi_ctx_s
{
	int			fd;
	char		dev[64];
	uint8_t		bits;
	uint16_t	delay;
	uint32_t	mode;
	uint32_t	speed;
}spi_ctx_t;

#define PROG_VERSION "1.0.0"

//spi初始化
static int spi_init(spi_ctx_t *spi_ctx);

//spi发送数据
static int transfer(spi_ctx_t *spi_ctx,uint8_t const *tx,uint8_t const *rx,size_t len );

static void program_usage(char *progname)
{
	printf("Usage:%s [OPTION]...\n",progname);
	printf(" %s is a program to test IGKBoard loop spi\n",progname);

	printf("\nMandatory arguments to long options are mandatory for short options too:\n");
	printf(" -d[device	] Specify SPI device , such as: /dev/spidev0.0\n");
	printf(" -s[speed	] max speed (HZ) , such as:-s 500000\n");
	printf(" -p[print   ]  Send data (such as: -p 1234/xde/xad)\n");

	printf("\n%s version %s\n", progname, PROG_VERSION);
	return;
}


int main (int argc, char **argv)
{
	int 		ret;
	spi_ctx_t	spi_ctx;
	char		*spi_dev = "/dev/spidev0.0";//默认设备
	uint32_t	spi_speed = 500000;			//默认速率500k
	char		*input_tx = "Hello LinXincheng";//默认发送数据
	uint8_t		rx_buffer[100];				//接收缓存
	int 		opt;
	char 		*progname = NULL;

	struct option long_options[]= {
		{"device",required_argument,NULL,'d'},
		{"speed", required_argument, NULL, 's'},
		{"print", required_argument, NULL, 'p'},
		{"help", no_argument, NULL, 'h'},
		{NULL, 0, NULL, 0}
	};

	progname = (char *)basename(argv[0]);

	while((opt = getopt_long(argc,argv,"d:s:p:h", long_options, NULL)) != -1)
	{
		switch(opt)
		{
			case 'd':
				spi_dev = optarg;
				break;

			case 's':
				spi_speed = atoi(optarg);
				break;

			case 'p':
				input_tx = optarg;
				break;

			case 'h':
				program_usage(progname);
				return 0;

			default:
				break;
		}
	}
	if( 0 == spi_speed || !input_tx)
	{
		program_usage(progname);
		return -1;
	}

	memset(&spi_ctx,0,sizeof(spi_ctx));
	strncpy(spi_ctx.dev,spi_dev,sizeof(spi_ctx.dev));

	spi_ctx.bits  = 8;//设置字长 8bit
	spi_ctx.delay = 100;//设置时延100us
	spi_ctx.mode  =	SPI_MODE_2;//设置spi模式
	spi_ctx.speed = spi_speed; //设置速率

	//spi设备初始化
	if( spi_init(&spi_ctx) < 0 )
	{	
		printf("spi_init error\n");
		return -1;
	}
	printf("spi [dev %s] [fd = %d] init successfully\n",spi_ctx.dev,spi_ctx.fd);

	//spi发送接收函数
	if( transfer(&spi_ctx,input_tx,rx_buffer,strlen(input_tx)) < 0)
	{
		printf("spi_transfer error\n");
		return -2;
	}

	/* 打印 tx_buffer 和 rx_buffer*/
	printf("tx_buffer: | %s |\n",input_tx);
	printf("rx_buffer: | %s |\n",rx_buffer);

	return 0;
} 

int transfer(spi_ctx_t *spi_ctx, uint8_t const *tx, uint8_t const *rx, size_t len)
{
	struct spi_ioc_transfer tr ={
		.tx_buf = (unsigned long )tx,
		.rx_buf = (unsigned long )rx,
		.len = len,
		.delay_usecs = spi_ctx->delay,
		.speed_hz = spi_ctx->speed,
		.bits_per_word = spi_ctx->bits,
	};

	//发送并接收一组数据
	if( ioctl(spi_ctx->fd, SPI_IOC_MESSAGE(1),&tr) < 0)
	{
		printf("ERROR:SPI transfer failure:%s\n",strerror(errno));
		return -1;
	}
	return 0;
}

/*  spi 初始化函数  */
int spi_init(spi_ctx_t *spi_ctx)
{
	int ret;
	spi_ctx->fd = open(spi_ctx->dev,O_RDWR);
	if( spi_ctx->fd < 0)
	{
		printf("open %s error\n",spi_ctx->dev);
		return -1;
	}

	//设置SPI 接收和发送的工作模式
	ret = ioctl(spi_ctx->fd, SPI_IOC_RD_MODE, &spi_ctx->mode);
	if( ret < 0)
	{
		printf("ERROR:SPI set SPI_IOC_RD_MODE [0x%x] failure:%s\n",spi_ctx->mode,strerror(errno));
		goto fd_close;
	}

	//设置SPI通信接收和发送的字长
	ret = ioctl(spi_ctx->fd, SPI_IOC_RD_BITS_PER_WORD, &spi_ctx->bits);
	if( ret < 0 )
	{
		printf("ERROR: SPI set SPI_IOC_RD_BITS_PER_WORD [%d] failure: %s\n ", spi_ctx->bits, strerror(errno));
		goto fd_close;
	}

	//设置SPI最高工作频率
	ret = ioctl(spi_ctx->fd,SPI_IOC_WR_MAX_SPEED_HZ,&spi_ctx->speed);
	if( ret == -1)
	{
		printf("ERROR: SPI set SPI_IOC_RD_MAX_SPEED_HZ [%d] failure: %s\n ", spi_ctx->speed, strerror(errno));
		goto fd_close;
	}

	printf("spi mode: 0x%x\n", spi_ctx->mode);
	printf("bits per word: %d\n", spi_ctx->bits);
	printf("max speed: %d Hz (%d KHz)\n", spi_ctx->speed, spi_ctx->speed / 1000);

	return spi_ctx->fd;

fd_close:

	close(spi_ctx->fd);
	return -1;
}


